package com.matrix.user.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.matrix.api.marketing.MarketingApi;
import com.matrix.api.user.UserApi;
import com.matrix.api.user.dto.UpdateNicknameDTO;
import com.matrix.api.user.dto.UserRegisterDTO;
import com.matrix.api.user.vo.UserInfoVO;
import com.matrix.api.user.vo.UserLoginInfoVO;
import com.matrix.common.constant.RedisConstants;
import com.matrix.common.domain.PageResult;
import com.matrix.common.domain.Result;
import com.matrix.common.enums.UserTypeEnum;
import com.matrix.common.exception.BusinessException;
import com.matrix.user.config.StorageProperties;
import com.matrix.user.converter.UserConverter;
import com.matrix.user.entity.User;
import com.matrix.user.entity.UserFavorite;
import com.matrix.user.mapper.UserFavoriteMapper;
import com.matrix.user.mapper.UserMapper;
import com.matrix.user.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.rpc.RpcException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 有点甜
 * @since 2025/7/25
 */
@Slf4j
@Service
@DubboService
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService, UserApi {

    private final PasswordEncoder passwordEncoder;
    private final Snowflake snowflake = IdUtil.getSnowflake();
    private final StorageProperties storageProperties;
    private final UserFavoriteMapper favoriteMapper;
    private final StringRedisTemplate redisTemplate;

    @DubboReference(check = false)
    private MarketingApi marketingApi;

    @Override
    @Transactional
    public void register(UserRegisterDTO registerDTO) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", registerDTO.getPhone());
        queryWrapper.eq("user_type", registerDTO.getUserType().name());
        if (this.count(queryWrapper) > 0) {
            throw new BusinessException("该手机号已注册过此类型的账号");
        }

        User user = new User();
        user.setUid(snowflake.nextId());
        user.setUserType(registerDTO.getUserType());
        user.setPhone(registerDTO.getPhone());
        user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
        user.setNickname("用户_" + user.getUid());
        user.setStatus(1);

        boolean isSuccess = this.save(user);
        if (!isSuccess) {
            throw new BusinessException("注册失败,请稍后再试");
        }

        grantNewUserCouponWithRetry(user.getUid());
    }

    @Override
    public List<Long> listFavoritesInternal(Long userId) {
        return favoriteMapper.selectList(new LambdaQueryWrapper<UserFavorite>()
                        .eq(UserFavorite::getUserId, userId)
                        .select(UserFavorite::getShopId))
                .stream().map(UserFavorite::getShopId).collect(Collectors.toList());
    }

    @Override
    public UserLoginInfoVO findByAccount(String account, UserTypeEnum userType) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", account);
        queryWrapper.eq("user_type", userType);

        User user = this.getOne(queryWrapper);

        return user != null ? mapUserToLoginInfoVO(user) : null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<UserInfoVO> updateUserProfile(Long uid, UpdateNicknameDTO profileDTO) {
        User user = this.getOne(new QueryWrapper<User>().eq("uid", uid));
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        user.setNickname(profileDTO.getNickname());
        this.updateById(user);

        UserInfoVO updatedUserInfo = getUserInfoByUid(uid);
        return Result.success(updatedUserInfo);
    }

    @Override
    public UserInfoVO getUserInfoByUid(Long uid) {
        User user = this.getOne(new QueryWrapper<User>().eq("uid", uid));
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        return UserConverter.INSTANCE.toUserInfoVO(user);
    }

    @Override
    public Map<Long, UserTypeEnum> getUserTypesByIds(List<Long> uids) {
        if (uids == null || uids.isEmpty()) {
            return Collections.emptyMap();
        }
        List<User> users = this.list(new LambdaQueryWrapper<User>()
                .in(User::getUid, uids)
                .select(User::getUid, User::getUserType));

        return users.stream().collect(Collectors.toMap(User::getUid, User::getUserType));
    }

    @Override
    public void updateUserAvatar(Long uid, String avatarUrl) {
        if (!avatarUrl.startsWith(storageProperties.getResourceUrlPrefix())) {
            throw new BusinessException("非法的头像URL");
        }

        User user = new User();
        user.setAvatar(avatarUrl);
        boolean updated = this.update(user, new LambdaQueryWrapper<User>().eq(User::getUid, uid));
        if (!updated) {
            throw new BusinessException("用户不存在或更新失败");
        }
    }

    @Override
    public List<UserInfoVO> getUsersByIds(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return Collections.emptyList();
        }

        List<User> users = this.list(new LambdaQueryWrapper<User>().in(User::getUid, ids));

        return UserConverter.INSTANCE.toUserInfoVOList(users);
    }

    @Override
    public void addFavorite(Long userId, Long shopId) {
        if (checkFavoriteStatus(userId, shopId)) {
            return;
        }
        UserFavorite favorite = new UserFavorite();
        favorite.setUserId(userId);
        favorite.setShopId(shopId);
        favoriteMapper.insert(favorite);
    }

    @Override
    public void removeFavorite(Long userId, Long shopId) {
        favoriteMapper.delete(new LambdaQueryWrapper<UserFavorite>()
                .eq(UserFavorite::getUserId, userId)
                .eq(UserFavorite::getShopId, shopId));
    }

    @Override
    public List<Long> listFavorites(Long userId) {
        return favoriteMapper.selectList(new LambdaQueryWrapper<UserFavorite>()
                        .eq(UserFavorite::getUserId, userId)
                        .orderByDesc(UserFavorite::getCreateTime))
                .stream().map(UserFavorite::getShopId).collect(Collectors.toList());
    }

    @Override
    public boolean checkFavoriteStatus(Long userId, Long shopId) {
        return favoriteMapper.selectCount(new LambdaQueryWrapper<UserFavorite>()
                .eq(UserFavorite::getUserId, userId)
                .eq(UserFavorite::getShopId, shopId)) > 0;
    }

    @Override
    public PageResult<UserInfoVO> listUsersByPage(int pageNum, int pageSize, String phone, String nickname) {
        Page<User> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserType, UserTypeEnum.USER);
        queryWrapper.like(StringUtils.hasText(phone), User::getPhone, phone);
        queryWrapper.like(StringUtils.hasText(nickname), User::getNickname, nickname);
        queryWrapper.orderByDesc(User::getCreateTime);
        this.page(page, queryWrapper);
        List<UserInfoVO> voList = UserConverter.INSTANCE.toUserInfoVOList(page.getRecords());
        return new PageResult<>(voList, page.getTotal(), page.getPages());
    }

    @Override
    public void updateUserStatus(Long userId, Integer status) {
        if (status != 0 && status != 1) {
            throw new BusinessException("无效的状态值");
        }
        User user = new User();
        user.setStatus(status);
        boolean updated = this.update(user, new LambdaQueryWrapper<User>().eq(User::getUid, userId));
        if (!updated) {
            throw new BusinessException("用户不存在或更新失败");
        }
        log.info("用户 {} 的状态已更新为: {}", userId, status);
    }

    @Override
    public List<String> getSearchHistory(Long userId) {
        String key = RedisConstants.buildKey(RedisConstants.USER_SEARCH_HISTORY_KEY_PREFIX, userId);
        Set<String> history = redisTemplate.opsForZSet().reverseRange(key, 0, 9);
        return history != null ? new ArrayList<>(history) : Collections.emptyList();
    }

    @Override
    public void addSearchHistory(Long userId, String keyword) {
        if (!StringUtils.hasText(keyword)) {
            return;
        }
        String key = RedisConstants.buildKey(RedisConstants.USER_SEARCH_HISTORY_KEY_PREFIX, userId);
        redisTemplate.opsForZSet().add(key, keyword, System.currentTimeMillis());
        redisTemplate.opsForZSet().removeRange(key, 0, -21);
    }

    @Override
    public void clearSearchHistory(Long userId) {
        redisTemplate.delete(RedisConstants.buildKey(RedisConstants.USER_SEARCH_HISTORY_KEY_PREFIX, userId));
    }


    private UserLoginInfoVO mapUserToLoginInfoVO(User user) {
        UserLoginInfoVO loginInfoVO = UserConverter.INSTANCE.toUserLoginInfoVO(user);
        loginInfoVO.setUserType(user.getUserType().name());
        return loginInfoVO;
    }

    public void grantNewUserCouponWithRetry(Long userId) {
        try {
            log.info("Attempting to grant new user coupon for userId: {}", userId);
            marketingApi.grantNewUserCoupon(userId);
        } catch (RpcException e) {
            log.error("Granting new user coupon for {} failed. Retrying...", userId, e);
            throw e;
        }
    }
}
